// Copyright 2008 Philip Allison <sane@not.co.uk>

//    This file is part of Infector.
//
//    Infector is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    Infector is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with Infector.  If not, see <http://www.gnu.org/licenses/>.


//
// Includes
//

// Standard
#ifdef HAVE_CONFIG_H
#	include "config.h"
#endif

// Language headers
#include <memory>
#include <utility>
#include <vector>
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <time.h>
#include <limits>
#include <stdio.h>

// Library headers
#include <glibmm.h>
#include <sigc++/sigc++.h>

// Project headers
#include "gametype.hxx"
#include "boardstate.hxx"
#include "ai.hxx"
#include "ai2.hxx"
#include "game.hxx"


//
// Implementation
//

AI2::AI2(Game *game, const BoardState *bs, const GameType *gt)
	: AI::AI(game, bs, gt)
{
	game->move_made.connect(sigc::mem_fun(this, &AI2::onMoveMade));
	
	// Seed the random number generator (moves are picked at random if there
	// are multiple possibilities with the same score, such as in the opening)
	srand(time(NULL));
	
	// Make a move if it's our turn first
	AI2::onMoveMade(0, 0, 0, 0, false);
}

bool AI2movecmp(const std::pair<move, int> &a, const std::pair<move, int> &b)
{
	return a.second > b.second;
}

void AI2::onMoveMade(const int start_x, const int start_y, const int end_x, const int end_y, const bool gameover)
{
	if (gameover)
		return;

	piece me = m_pBoardState->getPlayer();
  std::cout << "me is: " << me << std::endl;
	if (m_pGameType->isPlayerType(me, pt_ai2))
	{
    std::cout << "doing something" << std::endl;
    move nextMove;    
    nextMove.source_x = -1;
		nextMove.source_y = -1;
		nextMove.dest_x = -1;
		nextMove.dest_y = -1;
    
    std::cout << alphaBeta(*m_pBoardState, 3, -50000, 50000, true, &nextMove, true, me) << std::endl;
    
		m = nextMove;
		
		// Highlight the square we're going to move then
		// make the actual move in 0.5 second time increments
		// (to let people see)
		selectpiece = true;
		Glib::signal_timeout().connect(sigc::mem_fun(this, &AI2::makeMove), 500);
	}
}

int AI2::alphaBeta(const BoardState node, int depth, int alpha, int beta, bool first, move* next, bool isMax, piece me)
{
  std::vector<move> preMoves = node.getPossibleMoves(me, false);

  if (depth == 0 || preMoves.size() == 0)
  {
    return heuristic(node, me);
  }
  
  BoardState track_b(node.getGameType());
  int val = 0;
  if (!isMax) {    
    for (std::vector<move>::iterator i = preMoves.begin(); i != preMoves.end(); ++i) {
      
      // Simulate the current move
      BoardState new_b(node);
      int adj = new_b.getAdjacency(i->source_x, i->source_y, i->dest_x, i->dest_y);
      if (adj == 2) {
	      new_b.setPieceAt(i->source_x, i->source_y, pc_player_none);
	    } else { 	
		    if (track_b.getPieceAt(i->dest_x, i->dest_y) == pc_tracking) {
			      continue;
		    }
		  track_b.setPieceAt(i->dest_x,i->dest_y, pc_tracking);
	    }

      piece other;
	    if (me == pc_player_1) other = pc_player_2;
	    else other = pc_player_1;     
      new_b.setPieceAt(i->dest_x, i->dest_y, other);  

      val = alphaBeta(new_b, depth - 1, alpha, beta, false, NULL, !isMax, me);
      if (val < beta) {
        beta = val;
      }
      if (alpha > beta) {
        break;
      }
    }
    return beta;
  } else
  {
    for (std::vector<move>::iterator i = preMoves.begin(); i != preMoves.end(); ++i)
    {
      // Simulate the current move
      BoardState new_b(node);
      new_b.setPieceAt(i->dest_x, i->dest_y, me);
      int adj = new_b.getAdjacency(i->source_x, i->source_y, i->dest_x, i->dest_y);
      if (adj == 2) {
        new_b.setPieceAt(i->source_x, i->source_y, pc_player_none);
      } else { 	
	      if (track_b.getPieceAt(i->dest_x, i->dest_y) == pc_tracking) {
		        continue;
	      }
	    track_b.setPieceAt(i->dest_x,i->dest_y, pc_tracking);
      }

      val = alphaBeta(new_b, depth - 1, alpha, beta, false, NULL, !isMax, me);
      if ((val == alpha) && (first) && (rand() % 2)) {
            //std::cout << "val: " << val << " alpha: " << alpha << std::endl;            
            //next->source_x = i->source_x;
           // next->source_y = i->source_y;
           // next->dest_x = i->dest_x;
           // next->dest_y = i->dest_y;          
      }
      if ( val > alpha)
      {
        alpha = val;
        if (first) 
        {
          next->source_x = i->source_x;
          next->source_y = i->source_y;
          next->dest_x = i->dest_x;
          next->dest_y = i->dest_y;
        }
      } 
      if (alpha > beta) {
        break;
      }
    }
    return alpha;
  }
  return 0;
}

int AI2::heuristic(const BoardState node, piece me)
{
  //piece me = node->getPlayer();
  int p1, p2, p3, p4;
  node.getScores(p1, p2, p3, p4);
  switch (me)
  {
    case pc_player_1:
      return p1 - p2;
    case pc_player_2:
      return p2 - p1;
  }
}
